#include "CpuZ80.h"
#include "CpuZ80Macros.h"
#include <cassert>

byte_t CpuZ80::HandleOpcode_ED(byte_t opcode)
{
	switch(opcode)
	{

	case 0x46: m_interruptMode = 0;					break;
	case 0x56: m_interruptMode = 1;					break;
	case 0x5E: m_interruptMode = 2;					break;

	// LD I, A
	case 0x47:
	{
		RegI = RegA;
	}												break;

	// LD A, I
	case 0x57:
	{
		SetFlag(Flag_Sign, Sign(RegI) > 0);
		SetFlag(Flag_Zero, RegI == 0);
		SetFlag(Flag_Parity, m_iff2);
		SetFlag(Flag_Negative, true);
		SetFlag(Flag_HC, false);

		SetFlag(Flag_F3, (RegA & static_cast<byte_t>(Flag_F3)) > 0);
		SetFlag(Flag_F5, (RegA & static_cast<byte_t>(Flag_F5)) > 0);

		RegA = RegI;
	}												break;

	case 0x67:
	{
		byte_t A = RegA;
		byte_t HL = ReadByte(RegHL);

		WriteByte((HL >> 4) | ((A & 0xF) << 4), RegHL);
		RegA = (RegA & 0xF0) | (HL & 0x0F);

		SetFlag(Flag_Negative, false);
		SetFlag(Flag_HC, false);
		SetFlag(Flag_Zero, RegA == 0);
		SetFlag(Flag_Parity, m_parityTable[RegA]);
		SetFlag(Flag_Sign, (RegA >= 0x7F) && (RegA <= 0xFF));

		SetFlag(Flag_F3, (RegA & static_cast<byte_t>(Flag_F3)) > 0);
		SetFlag(Flag_F5, (RegA & static_cast<byte_t>(Flag_F5)) > 0);

	}												break; // RRD

	case 0x6F:
	{
		byte_t A = RegA;
		byte_t HL = ReadByte(RegHL);

		WriteByte((HL << 4) | (A & 0x0F), RegHL);
		RegA = (RegA & 0xF0) | ((HL & 0xF0) >> 4);

		SetFlag(Flag_Negative, false);
		SetFlag(Flag_HC, false);
		SetFlag(Flag_Zero, RegA == 0);
		SetFlag(Flag_Parity, m_parityTable[RegA]);
		SetFlag(Flag_Sign, (RegA >= 0x7F) && (RegA <= 0xFF));

		SetFlag(Flag_F3, (RegA & static_cast<byte_t>(Flag_F3)) > 0);
		SetFlag(Flag_F5, (RegA & static_cast<byte_t>(Flag_F5)) > 0);
	}												break; // RLD

	case 0xA1:
	{
		byte_t data = ReadByte(RegHL);
		word_t res = RegA - data;

		RegHL++;
		RegBC--;

		SetFlag(Flag_Sign, (res & 0x80) > 0);
		SetFlag(Flag_Zero, (res & 0xFF) == 0);
		SetFlag(Flag_HC, (RegA & 0x0F) < (data & 0x0F));
		SetFlag(Flag_Parity, RegBC > 0);
		SetFlag(Flag_Negative, true);

		byte_t temp = RegA - data;
		if (IsFlagSet(Flag_HC)) temp--;
		SetFlag(Flag_F3, (temp & static_cast<byte_t>(Bit_3)) > 0);
		SetFlag(Flag_F5, (temp & static_cast<byte_t>(Bit_1)) > 0);
	}												break; // CPI

	case 0xA9:
	{
		byte_t data = ReadByte(RegHL);
		word_t res = RegA - data;

		RegHL--;
		RegBC--;

		SetFlag(Flag_Sign, (res & 0x80) > 0);
		SetFlag(Flag_Zero, (res & 0xFF) == 0);
		SetFlag(Flag_HC, (RegA & 0x0F) < (data & 0x0F));
		SetFlag(Flag_Parity, RegBC > 0);
		SetFlag(Flag_Negative, true);

		byte_t temp = RegA - data;
		if (IsFlagSet(Flag_HC)) temp--;
		SetFlag(Flag_F3, (temp & static_cast<byte_t>(Bit_3)) > 0);
		SetFlag(Flag_F5, (temp & static_cast<byte_t>(Bit_1)) > 0);
	}												break; // CPD

	case 0xB1:
	{
		byte_t data = ReadByte(RegHL);
		word_t res = RegA - data;

		RegHL++;
		RegBC--;

		SetFlag(Flag_Sign, (res & 0x80) > 0);
		SetFlag(Flag_Zero, (res & 0xFF) == 0);
		SetFlag(Flag_HC, (RegA & 0x0F) < (data & 0x0F));
		SetFlag(Flag_Parity, RegBC > 0);
		SetFlag(Flag_Negative, true);

		if (RegBC > 0 && RegA != data)
			RegPC -= 2;

		byte_t temp = RegA - data;
		if (IsFlagSet(Flag_HC)) temp--;
		SetFlag(Flag_F3, (temp & static_cast<byte_t>(Bit_3)) > 0);
		SetFlag(Flag_F5, (temp & static_cast<byte_t>(Bit_1)) > 0);
	}												break; // CPIR

	case 0xB9:
	{
		byte_t data = ReadByte(RegHL);
		word_t res = RegA - data;

		RegHL--;
		RegBC--;

		SetFlag(Flag_Sign, (res & 0x80) > 0);
		SetFlag(Flag_Zero, (res & 0xFF) == 0);
		SetFlag(Flag_HC, (RegA & 0x0F) < (data & 0x0F));
		SetFlag(Flag_Parity, RegBC > 0);
		SetFlag(Flag_Negative, true);

		if (RegBC > 0 && RegA != data)
			RegPC -= 2;

		byte_t temp = RegA - data;
		if (IsFlagSet(Flag_HC)) temp--;
		SetFlag(Flag_F3, (temp & static_cast<byte_t>(Bit_3)) > 0);
		SetFlag(Flag_F5, (temp & static_cast<byte_t>(Bit_1)) > 0);
	}												break; // CPDR

	case 0xA0:
	{
		byte_t data = ReadByte(RegHL);
		WriteByte(data, RegDE);

		RegDE++;
		RegHL++;
		RegBC--;

		SetFlag(Flag_HC, false);
		SetFlag(Flag_Negative, false);
		SetFlag(Flag_Parity, RegBC > 0);

		byte_t temp = RegA + data;
		SetFlag(Flag_F3, (temp & static_cast<byte_t>(Bit_3)) > 0);
		SetFlag(Flag_F5, (temp & static_cast<byte_t>(Bit_1)) > 0);
	}												break; // LDI

	case 0xB0:
	{
		byte_t data = ReadByte(RegHL);
		WriteByte(data, RegDE);

		RegDE++;
		RegHL++;
		RegBC--;

		if (RegBC > 0) RegPC -= 2;

		SetFlag(Flag_HC, false);
		SetFlag(Flag_Negative, false);
		SetFlag(Flag_Parity, false);

		byte_t temp = RegA + data;
		SetFlag(Flag_F3, (temp & static_cast<byte_t>(Bit_3)) > 0);
		SetFlag(Flag_F5, (temp & static_cast<byte_t>(Bit_1)) > 0);
	}												break; // LDIR

	case 0xA8:
	{
		byte_t data = ReadByte(RegHL);
		WriteByte(data, RegDE);

		RegDE--;
		RegHL--;
		RegBC--;

		SetFlag(Flag_HC, false);
		SetFlag(Flag_Negative, false);
		SetFlag(Flag_Parity, RegBC > 0);

		byte_t temp = RegA + data;
		SetFlag(Flag_F3, (temp & static_cast<byte_t>(Bit_3)) > 0);
		SetFlag(Flag_F5, (temp & static_cast<byte_t>(Bit_1)) > 0);
	}												break; // LDD

	case 0xB8:
	{
		byte_t data = ReadByte(RegHL);
		WriteByte(data, RegDE);
		
		RegDE--;
		RegHL--;		
		RegBC--;

		if (RegBC > 0) RegPC -= 2;

		SetFlag(Flag_HC, false);
		SetFlag(Flag_Negative, false);
		SetFlag(Flag_Parity, false);

		byte_t temp = RegA + data;
		SetFlag(Flag_F3, (temp & static_cast<byte_t>(Bit_3)) > 0);
		SetFlag(Flag_F5, (temp & static_cast<byte_t>(Bit_1)) > 0);
	}												break; // LDDR	

	case 0xA2:
	{
		WriteByte(ReadBytePort(RegC), RegHL);
		RegHL++;
		RegB--;

		SetFlag(Flag_Negative, true);
		SetFlag(Flag_Zero, RegB == 0);
	}												break; // INI


	case 0xA3:
	{
		WriteBytePort(ReadByte(RegHL), RegC);
		RegHL++;
		RegB--;

		SetFlag(Flag_Negative, true);
		SetFlag(Flag_Zero, RegB == 0);
	}												break; // OUTI

	case 0xAA:
	{
		WriteByte(ReadBytePort(RegC), RegHL);
		RegHL--;
		RegB--;

		SetFlag(Flag_Negative, true);
		SetFlag(Flag_Zero, RegB == 0);
	}												break; // IND

	case 0xAB:
	{
		WriteBytePort(ReadByte(RegHL), RegC);
		RegHL--;
		RegB--;

		SetFlag(Flag_Negative, true);
		SetFlag(Flag_Zero, RegB == 0);
	}												break; // OUTD


	case 0x40: RegB = ReadBytePort(RegC);			break; // IN B,(C)
	case 0x48: RegC = ReadBytePort(RegC);			break; // IN C,(C)
	case 0x50: RegD = ReadBytePort(RegC);			break; // IN D,(C)
	case 0x58: RegE = ReadBytePort(RegC);			break; // IN E,(C)
	case 0x60: RegH = ReadBytePort(RegC);			break; // IN H,(C)
	case 0x68: RegL = ReadBytePort(RegC);			break; // IN L,(C)
	case 0x78: RegA = ReadBytePort(RegC);			break; // IN A,(C)

	case 0x41: WriteBytePort(RegB, RegC);			break; // OUT (C),B
	case 0x49: WriteBytePort(RegC, RegC);			break; // OUT (C),C
	case 0x51: WriteBytePort(RegD, RegC);			break; // OUT (C),D
	case 0x59: WriteBytePort(RegE, RegC);			break; // OUT (C),E
	case 0x61: WriteBytePort(RegH, RegC);			break; // OUT (C),H
	case 0x69: WriteBytePort(RegL, RegC);			break; // OUT (C),L
	case 0x79: WriteBytePort(RegA, RegC);			break; // OUT (C),A

	case 0x42: RegHL = SubWordCarry(RegHL, RegBC);	break; // SBC HL,BC 
	case 0x52: RegHL = SubWordCarry(RegHL, RegDE);	break; // SBC HL,DE 
	case 0x62: RegHL = SubWordCarry(RegHL, RegHL);	break; // SBC HL,HL 
	case 0x72: RegHL = SubWordCarry(RegHL, RegSP);	break; // SBC HL,SP 

	case 0x4A: RegHL = AddWordCarry(RegHL, RegBC);	break; // ADC HL,BC
	case 0x5A: RegHL = AddWordCarry(RegHL, RegDE);	break; // ADC HL,DE
	case 0x6A: RegHL = AddWordCarry(RegHL, RegHL);	break; // ADC HL,HL
	case 0x7A: RegHL = AddWordCarry(RegHL, RegSP);	break; // ADC HL,SP

	case 0x43: WriteWord(RegBC, FetchWord());		break; // LD (NN),BC
	case 0x53: WriteWord(RegDE, FetchWord());		break; // LD (NN),DE
	case 0x73: WriteWord(RegSP, FetchWord());		break; // LD (NN),SP

	case 0x4B: RegBC = ReadWord(FetchWord());		break; // LD BC,(NN)
	case 0x5B: RegDE = ReadWord(FetchWord());		break; // LD DE,(NN)
	case 0x7B: RegSP = ReadWord(FetchWord());		break; // LD SP,(NN)

	// NEG
	case 0x44:
	{
		byte_t oldA = RegA;
		RegA = -RegA;

		SetFlag(Flag_Sign, (RegA & 0x80) > 0);
		SetFlag(Flag_Zero, RegA == 0);
		SetFlag(Flag_HC, (oldA & 0x0F) > 0);
		SetFlag(Flag_Overflow, oldA == 0x80);
		SetFlag(Flag_Negative, true);
		SetFlag(Flag_Carry, oldA > 0);

		SetFlag(Flag_F3, (RegA & static_cast<byte_t>(Flag_F3)) > 0);
		SetFlag(Flag_F5, (RegA & static_cast<byte_t>(Flag_F5)) > 0);
	}												break; 

	default:
		assert(false && "Unrecognized Opcode");
		break;
	}

	return m_cyclesED[opcode];
}